package com.easy.consumer.component;

import com.alibaba.fastjson.JSON;
import com.easy.common.bean.*;
import com.easy.common.exception.ServiceException;
import com.easy.common.service.*;
import com.easy.common.util.SKRequest;
import com.easy.common.util.SYS;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

@Component

public class SKConsumer {
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @DubboReference
    ISeckillResultService seckillResultService;
    @DubboReference
    ISeckillOrderService seckillOrderService;
    @DubboReference
    ISeckillGoodsService seckillGoodsService;
    @DubboReference
    IOrderService orderService;
    @DubboReference
    IGoodsService goodsService;


    /**
     * 监听秒杀请求队列
     * @param message
     */
    @RabbitListener(queues=SYS.SK_QUEUE)//监听秒杀请求队列   监听者设计模式 被监听者有一个集合,记录监听者
    public void consumerMessage(String message){
        try {
            //将队列中的JSON转换成SKRequest对象
            SKRequest skRequest = JSON.parseObject(message, SKRequest.class);
            //判断是否有秒杀商品存在
            ValueOperations vos = redisTemplate.opsForValue();//处理字符串值
            HashOperations hos =redisTemplate.opsForHash();//处理hash值
            SetOperations sos = redisTemplate.opsForSet();//处理set值
            //秒杀商品的key
            Integer skgoodsid = skRequest.getSkgoodsid();
            String sk_goods_key = SYS.SK_PRE_GOODS_KEY + skgoodsid;//redis中存储的秒杀商品的key对应val
            //秒杀活动的key
            Integer activityid = skRequest.getActivityid();
            String sk_activity_key = SYS.SK_PRE_ACTIVITY_KEY + activityid;

            boolean hasActivity = redisTemplate.hasKey(sk_activity_key);//检查是否有这个key
            int activity_status = (int)hos.get(sk_activity_key,SYS.SK_ACTIVITY_STATUS_HASH_KEY);//秒杀活动存的hash值，有三个属性，活动状态，开始时间，结束时间
            long activity_start_time = (long) hos.get(sk_activity_key,SYS.SK_ACTIVITY_START_TIME_KEY);
            long activity_end_time = (long) hos.get(sk_activity_key,SYS.SK_ACTIVITY_END_TIME_KEY);
            if(!hasActivity||activity_status!=1||System.currentTimeMillis()<activity_start_time||System.currentTimeMillis()>activity_end_time){
                //活动不存在||活动状态异常||活动没有开始||活动已经结束
                //秒杀失败的信息,将数据保存到秒杀记录表中,封装成秒杀记录的数据,转发到死信队列中
                rabbitTemplate.convertAndSend(SYS.SK_EXCHANGE, SYS.SK_DLX_ROUTING_KEY, message);
                //结束流程
                return;
            }
            //准备秒杀
            //获取分布式锁
            //某一个秒杀商品的分布式锁的key
            String sk_lock_key=SYS.SK_PRE_LOCK_KEY + skgoodsid;
            boolean lockAcquired = false;
            int retryCount = 0;
            while (!lockAcquired && retryCount < 3) {//尝试获取锁，最多尝试3次
                lockAcquired = vos.setIfAbsent(sk_lock_key,"",SYS.SK_LOCK_EXPIRE_TIME, TimeUnit.MILLISECONDS);
                if (!lockAcquired) {
                    retryCount++;
                    try {
                        Thread.sleep(10); // 短暂等待后重试
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }

            // 如果无法获取锁，将请求转到死信队列
            if (!lockAcquired) {
                rabbitTemplate.convertAndSend(SYS.SK_EXCHANGE, SYS.SK_DLX_ROUTING_KEY, message);
                return;
            }
//            while (!vos.setIfAbsent(sk_lock_key,"",SYS.SK_LOCK_EXPIRE_TIME, TimeUnit.MILLISECONDS));   //76-95代替了这行
            long a=System.currentTimeMillis();
            //检查用户是否已经抢购 --  用户允许抢购的数量是可以在抢购商品中设置的
            if(sos.isMember(SYS.SK_RESULT_GOODS_SET_KEY+skgoodsid,skRequest.getUserid())){
                //已经抢购了
                rabbitTemplate.convertAndSend(SYS.SK_EXCHANGE, SYS.SK_DLX_ROUTING_KEY, message);
                //开放分布式锁
                redisTemplate.delete(sk_lock_key);
                //结束流程
                return;
            }
            //秒杀成功在redis中记录秒杀用户的信息，防止超买
            sos.add(SYS.SK_RESULT_GOODS_SET_KEY+skgoodsid,skRequest.getUserid());

            //检查秒杀商品数量,如果数量不足,转发死信，放弃分布式锁,结束流程
            if (vos.get(sk_goods_key) == null || Integer.parseInt(vos.get(sk_goods_key).toString()) <= 0) {
                //秒杀没有数量，转发死信队列
                rabbitTemplate.convertAndSend(SYS.SK_EXCHANGE, SYS.SK_DLX_ROUTING_KEY, message);
                //释放分布式锁
                redisTemplate.delete(sk_lock_key);
                //结束流程
                return;
            }

            //先扣数量
            redisTemplate.opsForValue().decrement(sk_goods_key);
            //放弃分布式锁
            redisTemplate.delete(sk_lock_key);
            long b=System.currentTimeMillis();
            System.out.println("秒杀成功，耗时："+(b-a));
            //秒杀记录和秒杀订单存储在数据库中
            //封装好SKResult对象
            SeckillResult seckillResult = new SeckillResult();
            seckillResult.setUser_id(skRequest.getUserid());
            seckillResult.setSeckill_goods_id(skRequest.getSkgoodsid());
            seckillResult.setActivity_id(skRequest.getActivityid());
            seckillResult.setResult(1);
            seckillResult.setId(skRequest.getSkid());
            seckillResult.setSeckill_time(LocalDateTime.now());
            seckillResultService.save(seckillResult);
            //封装订单对象
            //订单
            Order order = new Order();
            order.setUser_id(skRequest.getUserid());
            order.setCreate_time(LocalDateTime.now());//创建时间
            order.setPay_time(LocalDateTime.now());//支付时间
            order.setStatus(0);//订单状态为待支付
            
            //获取秒杀商品数据
            SeckillGoods seckillGoods = seckillGoodsService.getSeckillGoodsById(skRequest.getSkgoodsid());
            Goods goods = goodsService.getGoodsById(seckillGoods.getGoods_id());
            
            // 设置订单商品信息
            order.setGoods_id(goods.getId());
            order.setGoods_name(goods.getName());
            order.setGoods_count(1);
            order.setGoods_price(seckillGoods.getSeckill_price());
            
            //对数据中的数据-1
            seckillGoods.setStock(seckillGoods.getStock()-1);
            seckillGoodsService.updateSeckillGoods(seckillGoods);

            //保存订单数据,order中id是自增长的,新增后order对象获取订单id
            orderService.save(order);
            Integer orderid = order.getId();
            //秒杀订单
            SeckillOrder seckillOrder = new SeckillOrder();//秒杀订单
            seckillOrder.setSeckill_goods_id(seckillGoods.getId());//秒杀商品id
            seckillOrder.setUser_id(skRequest.getUserid());//用户id
            seckillOrder.setActivity_id(skRequest.getActivityid());
            seckillOrder.setCreate_time(LocalDateTime.now());
            seckillOrderService.save(seckillOrder);
            return;
        }catch(Exception e){
            //失败,将消息转到死信队列
            rabbitTemplate.convertAndSend(SYS.SK_EXCHANGE, SYS.SK_DLX_ROUTING_KEY, message);
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * 监听死信队列  秒杀失败的消息队列
     * @param message
     */
    @RabbitListener(queues=SYS.SK_DLX_QUEUE)
    public void consumerDLXMessage(String message){
        //在秒杀记录表中添加一条秒杀失败的记录
        try {
            SKRequest skRequest = JSON.parseObject(message, SKRequest.class);
            SeckillResult seckillResult = new SeckillResult();
            seckillResult.setId(skRequest.getSkid());
            seckillResult.setUser_id(skRequest.getUserid());
            seckillResult.setSeckill_goods_id(skRequest.getSkgoodsid());
            seckillResult.setActivity_id(skRequest.getActivityid());
            seckillResult.setResult(0); // 秒杀失败
            seckillResult.setSeckill_time(LocalDateTime.now());
            seckillResultService.save(seckillResult);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
